﻿Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.Data
Imports System.Text
Imports System.Windows.Forms
Imports System.Collections

Namespace manytomanytest
    <DebuggerStepThrough()> _
    Public Class BoundCheckedListBox
        Inherits CheckedListBox

        ''' <summary>
        ''' helper class. makes it possible to hold display and values information in an item of listbox which makes identification easier
        ''' </summary>
        <DebuggerStepThrough()> _
        Friend Class ListBoxItem
            Private _val As Object, _display As Object

            Public Property Value() As Object
                Get
                    Return _val
                End Get
                Set(ByVal value As Object)
                    _val = value
                End Set
            End Property

            Public Property Display() As Object
                Get
                    Return _display
                End Get
                Set(ByVal value As Object)
                    _display = value
                End Set
            End Property

            Public Overloads Overrides Function ToString() As String
                Return _display.ToString()
            End Function

            Public Sub New(ByVal value As Object, ByVal display As Object)
                Me._display = display
                Me._val = value
            End Sub
        End Class

        Private m_parentDataSource As Object, m_childDataSource As Object, m_relationDataSource As Object
        Private m_parentIDMember As String, m_childIDMember As String, m_childDisplayMember As String, m_parentValueMember As String, m_childValueMember As String

        Private childListChangedHandler As ListChangedEventHandler
        Private parentListChangedHandler As ListChangedEventHandler
        Private relationListChangedHandler As ListChangedEventHandler
        Private itemCheckChangesHandler As ItemCheckEventHandler
        Private selectionChangedHandler As EventHandler

        Private childPositionChangedHandler As EventHandler
        Private parentPositionChangedHandler As EventHandler

        Private WithEvents childDataManager As CurrencyManager
        Private parentDataManager As CurrencyManager
        Private relationDataManager As CurrencyManager
        'private CurrencyManager myRelationDataManager;

        Private ready As Boolean = False

#Region "Properties"
#Region "DataSource"
        ''' <summary>
        ''' Returns or sets the datasource used as child datasource
        ''' </summary>
        <TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")> _
        <Category("Data")> _
        <Description("Gibt die Datenquelle (child) für das Control an.")> _
        Public Property ChildDataSource() As Object
            Get
                Return Me.m_childDataSource
            End Get
            Set(ByVal value As Object)
                If Me.m_childDataSource IsNot value Then
                    Me.m_childDataSource = value
                    tryDataBinding()
                End If
            End Set
        End Property
        ''' <summary>
        ''' Returns or sets the datasource used as parent datasource
        ''' </summary>
        <TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")> _
        <Category("Data")> _
        <Description("Gibt die Datenquelle (parent) für das Control an.")> _
        Public Property ParentDataSource() As Object
            Get
                Return Me.m_parentDataSource
            End Get
            Set(ByVal value As Object)
                If Me.m_parentDataSource IsNot value Then
                    Me.m_parentDataSource = value
                    tryDataBinding()
                End If
            End Set
        End Property
        ''' <summary>
        ''' Returns or sets the datasource used as relation datasource
        ''' </summary>
        <TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")> _
        <Category("Data")> _
        <Description("Gibt die Datenquelle (relation) für das Control an.")> _
        Public Property RelationDataSource() As Object
            Get
                Return Me.m_relationDataSource
            End Get
            Set(ByVal value As Object)
                If Me.m_relationDataSource IsNot value Then
                    Me.m_relationDataSource = value
                    tryDataBinding()
                End If
            End Set
        End Property
#End Region

#Region "DataMember"
        ''' <summary>
        ''' Returns or sets the ID column name of the parent datasource
        ''' </summary>
        <Category("Data")> _
        <Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", "System.Drawing.Design.UITypeEditor, System.Drawing")> _
        <Description("Gibt eine untergeordnete Liste von DataSource an, um sie anzuzeigen. - parent ID column")> _
        Public Property ParentIDMember() As String
            Get
                Return Me.m_parentIDMember
            End Get
            Set(ByVal value As String)
                If Me.m_parentIDMember <> value Then
                    Me.m_parentIDMember = value
                    tryDataBinding()
                End If
            End Set
        End Property
        ''' <summary>
        ''' Returns or sets the ID column name of the child datasource
        ''' </summary>
        <Category("Data")> _
        <Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", "System.Drawing.Design.UITypeEditor, System.Drawing")> _
        <Description("Gibt eine untergeordnete Liste von DataSource an, um sie anzuzeigen. - child ID column")> _
        Public Property ChildIDMember() As String
            Get
                Return Me.m_childIDMember
            End Get
            Set(ByVal value As String)
                If Me.m_childIDMember <> value Then
                    Me.m_childIDMember = value
                    tryDataBinding()
                End If
            End Set
        End Property
        ''' <summary>
        ''' Returns or sets the column name of the column which holds the informations used as names in the listbox
        ''' of the child datasource
        ''' </summary>
        <Category("Data")> _
        <Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", "System.Drawing.Design.UITypeEditor, System.Drawing")> _
        <Description("Gibt eine untergeordnete Liste von DataSource an, um sie anzuzeigen. - child display column")> _
        Public Property ChildDisplayMember() As String
            Get
                Return Me.m_childDisplayMember
            End Get
            Set(ByVal value As String)
                If Me.m_childDisplayMember <> value Then
                    Me.m_childDisplayMember = value
                    tryDataBinding()
                End If
            End Set
        End Property
        ''' <summary>
        ''' Returns or sets the column name of the relation table column which holds the foreign keys for the parent table
        ''' </summary>
        <Category("Data")> _
        <Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", "System.Drawing.Design.UITypeEditor, System.Drawing")> _
        <Description("Gibt eine untergeordnete Liste von DataSource an, um sie anzuzeigen. - parent foreign key column in relation")> _
        Public Property ParentValueMember() As String
            Get
                Return Me.m_parentValueMember
            End Get
            Set(ByVal value As String)
                If Me.m_parentValueMember <> value Then
                    Me.m_parentValueMember = value
                    tryDataBinding()
                End If
            End Set
        End Property
        ''' <summary>
        ''' Returns or sets the column name of the relation table column which holds the foreign keys for the child table
        ''' </summary>
        <Category("Data")> _
        <Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", "System.Drawing.Design.UITypeEditor, System.Drawing")> _
        <Description("Gibt eine untergeordnete Liste von DataSource an, um sie anzuzeigen. - child foreign key column in relation")> _
        Public Property ChildValueMember() As String
            Get
                Return Me.m_childValueMember
            End Get
            Set(ByVal value As String)
                If Me.m_childValueMember <> value Then
                    Me.m_childValueMember = value
                    tryDataBinding()
                End If
            End Set
        End Property
#End Region

#Region "CurrencyManager"
        ''' <summary>
        ''' Ruft den CurrencyManager der gebundenen Liste ab.
        ''' </summary>
        Protected ReadOnly Property ParentCurrencyManager() As CurrencyManager
            Get
                Return Me.parentDataManager
            End Get
        End Property
        ''' <summary>
        ''' Ruft den CurrencyManager der gebundenen Liste ab.
        ''' </summary>
        Protected ReadOnly Property ChildCurrencyManager() As CurrencyManager
            Get
                Return Me.childDataManager
            End Get
        End Property
        ''' <summary>
        ''' Ruft den CurrencyManager der gebundenen Liste ab.
        ''' </summary>
        Protected ReadOnly Property RelationCurrencyManager() As CurrencyManager
            Get
                Return Me.relationDataManager
            End Get
        End Property

#End Region
#End Region

        ''' <summary>
        ''' Renew the Databinding. BindingContext is changed, if you change the Parent.
        ''' </summary>
        Protected Overloads Overrides Sub OnBindingContextChanged(ByVal e As EventArgs)
            Me.tryDataBinding()
            MyBase.OnBindingContextChanged(e)
        End Sub

        ''' <summary>
        ''' Constructor. creates event handlers
        ''' </summary>
        Public Sub New()
            childListChangedHandler = New ListChangedEventHandler(AddressOf dataManager_childListChanged)
            childPositionChangedHandler = New EventHandler(AddressOf dataManager_childPositionChanged)

            parentListChangedHandler = New ListChangedEventHandler(AddressOf dataManager_parentListChanged)
            parentPositionChangedHandler = New EventHandler(AddressOf dataManager_parentPositionChanged)

            relationListChangedHandler = New ListChangedEventHandler(AddressOf dataManager_relationListChanged)
            'childPositionChangedHandler = new EventHandler(dataManager_childPositionChanged);

            itemCheckChangesHandler = New ItemCheckEventHandler(AddressOf BoundCheckedListBox_ItemCheck)
            'dmn
            AddHandler Me.ItemCheck, AddressOf BoundCheckedListBox_ItemCheck
            'Me.ItemCheck += itemCheckChangesHandler
            selectionChangedHandler = New EventHandler(AddressOf BoundCheckedListBox_SelectedIndexChanged)
            'dmn
            AddHandler Me.SelectedIndexChanged, AddressOf BoundCheckedListBox_SelectedIndexChanged
            'Me.SelectedIndexChanged += selectionChangedHandler
        End Sub

#Region "events"
#Region "listbox-fired events"
        ''' <summary>
        ''' Is called when (un)checking items of the listbox. updates the datasource relation table
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        Private Sub BoundCheckedListBox_ItemCheck(ByVal sender As Object, ByVal e As ItemCheckEventArgs)
            If Not ready Then
                Return
            End If

            If e.Index < 0 OrElse e.Index >= Me.Items.Count Then
                Return
            End If

            'just in case someone adds items manually
            'do not do that!
            'dmn 
            'If Me.Items(e.Index).[GetType]() <> GetType(ListBoxItem) Then
            ' Return
            ' End If

            'find the currently selected ID in the parent table
            Dim parentID As Object = Nothing
            Dim childID As Object = DirectCast(Me.Items(e.Index), ListBoxItem).Value
            Try
                'do we always have type datarowview?!
                'another solution is parentDataManager.GetItemProperties().Find(parentIDMember, false).GetValue(parentDataManager.List[parentDataManager.Position]);
                'currencymanager.current shows buggy behavior sometimes when used with tostring()
                Dim drv As DataRowView = DirectCast(Me.parentDataManager.Current, DataRowView)
                parentID = drv(Me.m_parentIDMember)
            Catch generatedExceptionName As Exception
            End Try

            If parentID Is Nothing OrElse childID Is Nothing Then
                Return
            End If
            Dim rows As New ArrayList()
            Dim relationProps As PropertyDescriptorCollection = relationDataManager.GetItemProperties()

            Dim parentValue As PropertyDescriptor = relationProps.Find(m_parentValueMember, False)
            Dim childValue As PropertyDescriptor = relationProps.Find(m_childValueMember, False)
            For i As Integer = 0 To relationDataManager.Count - 1

                'find row(s) with current parentID and childId of item being (un)checked
                Dim row As Object = relationDataManager.List(i)
                Dim val As Object = parentValue.GetValue(row)
                If Not parentID.Equals(val) Then
                    Continue For
                End If

                val = childValue.GetValue(row)

                If childID.Equals(val) Then
                    rows.Add(row)
                End If
            Next

            If e.NewValue = CheckState.Checked Then
                'case: changing to checked state
                Dim exists As Boolean = False
                For Each curRow As DataRowView In rows
                    'just in case of more than one row with same IDs, keep only one
                    If exists Then
                        curRow.Delete()
                    End If

                    'TODO: we should rather undelete deleted rows than always create new ones and delete them
                    'means we might end in having several deleted rows with same ids
                    'but how to get hands on 'em?! maybe through dataview or sth.
                    'if (curRow.Row.RowState == DataRowState.Deleted)
                    ' curRow.Row.
                    ' continue;


                    If curRow.Row.RowState <> DataRowState.Deleted AndAlso curRow.Row.RowState <> DataRowState.Detached Then
                        exists = True
                    End If
                Next
                'if none was found, which should be the normal case, create one
                'attention! assign allownull to your colums in your dataset or assign proper default values
                If Not exists Then
                    relationDataManager.AddNew()
                    DirectCast(relationDataManager.Current, DataRowView)(m_parentValueMember) = parentID
                    DirectCast(relationDataManager.Current, DataRowView)(m_childValueMember) = childID
                    relationDataManager.EndCurrentEdit()
                End If
            ElseIf e.NewValue = CheckState.Unchecked Then
                'case: changing to unchecked state
                'delete all concerned rows
                For Each curRow As DataRowView In rows
                    If curRow.Row.RowState = DataRowState.Deleted Then
                        Continue For
                    Else
                        curRow.Delete()
                    End If
                Next
            End If
        End Sub
        ''' <summary>
        ''' Is called when selcted item of listbox changed. updates position of the child table
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        Private Sub BoundCheckedListBox_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
            Try
                If Not Me.ready OrElse Me.SelectedItem Is Nothing OrElse _
                    (Me.SelectedItem.GetType() IsNot GetType(ListBoxItem)) Then
                    Return

                End If
            Catch
                'if selecteditem throws array out of bounds exception
                Return
            End Try

            Dim val As Object = DirectCast(Me.SelectedItem, ListBoxItem).Value

            Dim childProps As PropertyDescriptorCollection = childDataManager.GetItemProperties()
            Dim childID As PropertyDescriptor = childProps.Find(m_childIDMember, False)
            For i As Integer = 0 To childDataManager.Count - 1
                Dim row As Object = childDataManager.List(i)
                Dim val2 As Object = childID.GetValue(row)
                If val.Equals(val2) Then
                    'dmn
                    'childDataManager.PositionChanged -= Me.childPositionChangedHandler
                    'childDataManager.Position = i
                    'childDataManager.PositionChanged += Me.childPositionChangedHandler
                    Return
                End If
            Next
        End Sub
#End Region
#Region "currencymanager-fired events"
#Region "from parentdatamanger"
        ''' <summary>
        ''' Is called when list of parentdatasource changes. updates checkboxes
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        Private Sub dataManager_parentListChanged(ByVal sender As Object, ByVal e As ListChangedEventArgs)
            If Not ready Then
                Return
            End If
            setHaken()
        End Sub
        ''' <summary>
        ''' Is called when position of parent list changes. updates checkboxes
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        Private Sub dataManager_parentPositionChanged(ByVal sender As Object, ByVal e As EventArgs)
            If Not ready Then
                Return
            End If
            setHaken()
        End Sub
#End Region
#Region "from childdatamanager"
        ''' <summary>
        ''' Is called when list of child datasource changes. updates listboxitems
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        Private Sub dataManager_childListChanged(ByVal sender As Object, ByVal e As ListChangedEventArgs)
            If Not ready Then
                Return
            End If
            Select Case e.ListChangedType
                Case ListChangedType.ItemAdded
                    Me.updateView()
                    'TODO: we could do this more precisely                    
                    Exit Select
                Case ListChangedType.ItemChanged
                    Me.updateView()
                    'TODO: we could do this more precisely
                    Exit Select
                Case ListChangedType.ItemDeleted
                    Me.updateView()
                    'TODO: we could do this more precisely
                    Exit Select
                Case ListChangedType.ItemMoved
                    Me.updateView()
                    Exit Select
                Case ListChangedType.PropertyDescriptorAdded
                    Exit Select
                Case ListChangedType.PropertyDescriptorChanged
                    If Not Me.checkManagers() Then
                        tryDataBinding()
                    End If
                    Exit Select
                Case ListChangedType.PropertyDescriptorDeleted
                    If Not Me.checkManagers() Then
                        tryDataBinding()
                    End If
                    Exit Select
                Case ListChangedType.Reset
                    tryDataBinding()
                    Exit Select
            End Select
        End Sub
        ''' <summary>
        ''' Is called when position of child list changes. updates selected item in listbox
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        Private Sub dataManager_childPositionChanged(ByVal sender As Object, ByVal e As EventArgs)
            If Not ready Then
                Return
            End If
            Me.selectItem()
        End Sub
#End Region
#Region "from relationdatamanager"
        ''' <summary>
        ''' Is called when list of relation datasource changes. updates checkboxes
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        Private Sub dataManager_relationListChanged(ByVal sender As Object, ByVal e As ListChangedEventArgs)
            If Not ready Then
                Return
            End If
            Select Case e.ListChangedType
                Case ListChangedType.ItemAdded
                    setHaken()
                    Exit Select
                Case ListChangedType.ItemChanged
                    setHaken()
                    Exit Select
                Case ListChangedType.ItemDeleted
                    setHaken()
                    Exit Select
                Case ListChangedType.ItemMoved
                    Exit Select
                Case ListChangedType.PropertyDescriptorAdded
                    Exit Select
                Case ListChangedType.PropertyDescriptorChanged
                    If Not Me.checkManagers() Then
                        tryDataBinding()
                    End If
                    Exit Select
                Case ListChangedType.PropertyDescriptorDeleted
                    If Not Me.checkManagers() Then
                        tryDataBinding()
                    End If
                    Exit Select
                Case ListChangedType.Reset
                    tryDataBinding()
                    Exit Select
            End Select
        End Sub
#End Region
#End Region
#End Region

#Region "trydatabinding functions"
        ''' <summary>
        ''' Main function for binding to currencymanagers and calling update functions for listbox.
        ''' Checks if everything is valid.
        ''' </summary>
        Private Sub tryDataBinding()
            If Not (tryRelationDataBinding() AndAlso tryParentDataBinding() AndAlso tryChildDataBinding() AndAlso checkManagers()) Then
                Me.Enabled = False
                Me.ready = False
            Else
                Me.Enabled = True
                Me.ready = True
                Me.updateView()
            End If
        End Sub
        ''' <summary>
        ''' tries to get currencymanager of parent datasource and binds to it
        ''' </summary>
        ''' <returns>success or fail</returns>
        Private Function tryParentDataBinding() As Boolean
            If Me.m_parentDataSource Is Nothing OrElse MyBase.BindingContext Is Nothing Then
                Return False
            End If

            Dim cm As CurrencyManager
            Try
                cm = DirectCast(MyBase.BindingContext(Me.m_parentDataSource), CurrencyManager)
            Catch generatedExceptionName As System.ArgumentException
                ' If no CurrencyManager was found
                Return False
            End Try
            If Me.parentDataManager IsNot cm Then
                ' Unwire the old CurrencyManager
                If Me.parentDataManager IsNot Nothing Then
                    'dmn
                    RemoveHandler parentDataManager.ListChanged, AddressOf dataManager_parentListChanged
                    RemoveHandler parentDataManager.PositionChanged, AddressOf dataManager_parentPositionChanged
                    'Me.parentDataManager.ListChanged -= parentListChangedHandler
                    'Me.parentDataManager.PositionChanged -= parentPositionChangedHandler
                End If
                Me.parentDataManager = cm
                ' Wire the new CurrencyManager
                If Me.parentDataManager IsNot Nothing Then
                    'dmn
                    AddHandler parentDataManager.ListChanged, AddressOf dataManager_parentListChanged
                    AddHandler parentDataManager.PositionChanged, AddressOf dataManager_parentPositionChanged
                    'Me.parentDataManager.ListChanged += parentListChangedHandler
                    'Me.parentDataManager.PositionChanged += parentPositionChangedHandler
                End If
            End If
            Return True
        End Function
        ''' <summary>
        ''' tries to get currencymanager of child datasource and binds to it
        ''' </summary>
        ''' <returns>success or fail</returns>
        Private Function tryChildDataBinding() As Boolean
            If Me.m_childDataSource Is Nothing OrElse MyBase.BindingContext Is Nothing Then
                Return False
            End If

            Dim cm As CurrencyManager
            Try
                cm = DirectCast(MyBase.BindingContext(Me.m_childDataSource), CurrencyManager)
            Catch generatedExceptionName As System.ArgumentException
                ' If no CurrencyManager was found
                Return False
            End Try
            If Me.childDataManager IsNot cm Then
                ' Unwire the old CurrencyManager
                If Me.childDataManager IsNot Nothing Then
                    'dmn
                    RemoveHandler childDataManager.ListChanged, AddressOf dataManager_childListChanged
                    RemoveHandler childDataManager.PositionChanged, AddressOf dataManager_childPositionChanged
                    'Me.childDataManager.ListChanged -= childListChangedHandler
                    'Me.childDataManager.PositionChanged -= childPositionChangedHandler
                End If
                Me.childDataManager = cm
                ' Wire the new CurrencyManager
                If Me.childDataManager IsNot Nothing Then
                    'dmn    
                    AddHandler childDataManager.ListChanged, AddressOf dataManager_childListChanged
                    AddHandler childDataManager.PositionChanged, AddressOf dataManager_childPositionChanged
                    'Me.childDataManager.ListChanged += childListChangedHandler
                    'Me.childDataManager.PositionChanged += childPositionChangedHandler
                End If
            End If
            Return True
        End Function
        ''' <summary>
        ''' tries to get currencymanager of relation datasource and binds to it
        ''' </summary>
        ''' <returns>success or fail</returns>
        Private Function tryRelationDataBinding() As Boolean
            If Me.m_relationDataSource Is Nothing OrElse MyBase.BindingContext Is Nothing Then
                Return False
            End If

            Dim cm As CurrencyManager
            Try
                cm = DirectCast(MyBase.BindingContext(Me.RelationDataSource), CurrencyManager)
            Catch generatedExceptionName As System.ArgumentException
                ' If no CurrencyManager was found
                Return False
            End Try
            If Me.relationDataManager IsNot cm Then
                ' Unwire the old CurrencyManager
                If Me.relationDataManager IsNot Nothing Then
                    'this.relationDataManager.PositionChanged -= relationPositionChangedHandler;
                    'dmn
                    RemoveHandler Me.relationDataManager.ListChanged, AddressOf dataManager_relationListChanged
                    'Me.relationDataManager.ListChanged -= relationListChangedHandler
                End If
                Me.relationDataManager = cm
                ' Wire the new CurrencyManager
                If Me.relationDataManager IsNot Nothing Then
                    'this.relationDataManager.PositionChanged += relationPositionChangedHandler;
                    'dmn
                    AddHandler Me.relationDataManager.ListChanged, AddressOf dataManager_relationListChanged
                    'Me.relationDataManager.ListChanged += relationListChangedHandler
                End If
            End If
            Return True
        End Function

#End Region

#Region "listbox sync functions"
        ''' <summary>
        ''' checks if all managers and column names = member are set and if columns exist in datasources
        ''' </summary>
        ''' <returns></returns>
        Private Function checkManagers() As Boolean
            If parentDataManager Is Nothing OrElse childDataManager Is Nothing OrElse relationDataManager Is Nothing Then
                Return False
            End If
            If m_childDisplayMember Is Nothing OrElse m_childValueMember Is Nothing OrElse m_childIDMember Is Nothing OrElse m_parentValueMember Is Nothing OrElse m_parentIDMember Is Nothing Then
                Return False
            End If

            If childDataManager.GetItemProperties().Find(m_childDisplayMember, False) Is Nothing OrElse childDataManager.GetItemProperties().Find(m_childIDMember, False) Is Nothing OrElse parentDataManager.GetItemProperties().Find(m_parentIDMember, False) Is Nothing OrElse relationDataManager.GetItemProperties().Find(m_childValueMember, False) Is Nothing OrElse relationDataManager.GetItemProperties().Find(m_parentValueMember, False) Is Nothing Then
                Return False
            End If

            Return True
        End Function

        ''' <summary>
        ''' checks the checkboxes according to datasource
        ''' </summary>
        Private Sub setHaken()
            If Not ready Then
                Return
            End If
            'dmn
            RemoveHandler Me.ItemCheck, AddressOf BoundCheckedListBox_ItemCheck
            'Me.ItemCheck -= itemCheckChangesHandler

            'find the currently selected ID in the parent table
            Dim parentID As Object = Nothing
            Try
                'do we always have type datarowview?!
                'another solution is parentDataManager.GetItemProperties().Find(parentIDMember, false).GetValue(parentDataManager.List[parentDataManager.Position]);
                'currencymanager.current shows buggy behavior sometimes
                Dim drv As DataRowView = DirectCast(Me.parentDataManager.Current, DataRowView)
                parentID = drv(Me.m_parentIDMember)
            Catch generatedExceptionName As Exception
            End Try
            If parentID Is Nothing Then
                Return
            End If

            Dim childValues As New ArrayList()
            Dim relationProps As PropertyDescriptorCollection = relationDataManager.GetItemProperties()

            Dim parentValue As PropertyDescriptor = relationProps.Find(m_parentValueMember, False)
            Dim childValue As PropertyDescriptor = relationProps.Find(m_childValueMember, False)
            For i As Integer = 0 To relationDataManager.Count - 1
                Dim row As Object = relationDataManager.List(i)
                Dim val As Object = parentValue.GetValue(row)
                If parentID.Equals(val) Then
                    childValues.Add(childValue.GetValue(row))
                End If
            Next
            For i As Integer = 0 To Me.Items.Count - 1


                If Me.Items(i).[GetType]() IsNot GetType(ListBoxItem) Then
                    Me.SetItemChecked(i, False)
                    Continue For
                End If

                If childValues.Contains(DirectCast(Me.Items(i), ListBoxItem).Value) Then
                    Me.SetItemChecked(i, True)
                Else
                    Me.SetItemChecked(i, False)
                End If
            Next
            'dmn
            AddHandler Me.ItemCheck, AddressOf BoundCheckedListBox_ItemCheck
            'Me.ItemCheck += itemCheckChangesHandler
        End Sub

        ''' <summary>
        ''' fills the listbox, checks and selects items
        ''' </summary>
        Private Sub updateView()
            If Not ready Then
                Return
            End If
            'liste füllen
            Me.Items.Clear()
            For i As Integer = 0 To childDataManager.Count - 1
                Me.Items.Add(getItem(i))
            Next
            'haken setzen
            setHaken()
            selectItem()
        End Sub

        ''' <summary>
        ''' Returns a <see cref="ListBoxItem"/> which holds ID and displaymember information of child datasource at given index.
        ''' </summary>
        ''' <param name="index">The index of the row.</param>
        ''' <returns>A item wich contains the data.</returns>
        Private Function getItem(ByVal index As Integer) As ListBoxItem
            Dim row As Object = childDataManager.List(index)
            Dim propColl As PropertyDescriptorCollection = childDataManager.GetItemProperties()

            'is this dangerous?!, != null?
            Return New ListBoxItem(propColl.Find(ChildIDMember, False).GetValue(row), propColl.Find(m_childDisplayMember, False).GetValue(row))
        End Function


        ''' <summary>
        ''' selects the item according to position of currencymanager of child datasource
        ''' </summary>
        Private Sub selectItem()
            If Not ready Then
                Return
            End If

            Me.ClearSelected()
            'dmn
            RemoveHandler Me.SelectedIndexChanged, AddressOf BoundCheckedListBox_SelectedIndexChanged
            'Me.SelectedIndexChanged -= selectionChangedHandler

            Try
                'do we always have type datarowview?!
                'another solution is childDataManager.GetItemProperties().Find(childIDMember, false).GetValue(childDataManager.List[childDataManager.Position]);
                'currencymanager.current shows buggy behavior sometimes when used with tostring()
                Dim drv As DataRowView = DirectCast(Me.childDataManager.Current, DataRowView)
                Dim childID As Object = drv(Me.m_childIDMember)
                For i As Integer = 0 To Me.Items.Count - 1

                    If Me.Items(i).[GetType]() IsNot GetType(ListBoxItem) Then
                        Continue For
                    End If

                    If DirectCast(Me.Items(i), ListBoxItem).Value.Equals(childID) Then
                        Me.SetSelected(i, True)
                        Exit For
                    End If

                Next
            Catch generatedExceptionName As Exception
            End Try
            'dmn
            AddHandler Me.SelectedIndexChanged, AddressOf BoundCheckedListBox_SelectedIndexChanged
            'Me.SelectedIndexChanged += selectionChangedHandler
        End Sub

#End Region

        Private Sub InitializeComponent()
            Me.SuspendLayout()
            '
            'BoundCheckedListBox
            '
            Me.Size = New System.Drawing.Size(120, 94)
            Me.ResumeLayout(False)

        End Sub
    End Class
End Namespace